package cn.org.xiaosheng.webclient.utils;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.ReactiveAdapterRegistry;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.client.MultipartBodyBuilder;
import org.springframework.util.MultiValueMap;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.CoreSubscriber;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

// 使用BodyInserters提供内嵌的表单数据
import static org.springframework.web.reactive.function.BodyInserters.*;

/**
 * @author XiaoSheng
 * @date 2023-11-22
 * @dec 使用WebClient作为技术底座, 封装WebClient作为技术请求技术底座
 */
@Configuration
@Slf4j
public class WebClientUtils {

    /**
     * Get请求, 带请求头, 带参
     * @param url
     * @param headers
     * @param params
     * @return
     */
    public JSONObject getRequest(String url,
                                 MultiValueMap<String, String> headers,
                                 Map<String, Object> params) {
        Mono<JSONObject> mono = webClientBuilder().defaultHeaders(httpHeaders -> {
            httpHeaders.addAll(headers);
        }).build()
                // 方法调用, WebClient中提供了多种方法
                .method(HttpMethod.GET)
                // 请求URL
                .uri(url, params)
                // 获取响应结果
                .retrieve()
                // 将结果转换为指定类型
                .bodyToMono(JSONObject.class)
                .doOnError(e -> log.error("Error occurred", e))
                .onErrorResume(e -> Mono.just(new JSONObject().fluentPut("message", "Fallback value")));
        // 使用subscribe异步订阅结果
        mono.subscribe(result -> System.out.println(result));
        // 使用block同步阻塞,返回响应结果
        return mono.block();
    }

    /**
     * Get请求, 带请求头, 带参
     * @param url
     * @param headers
     * @param params
     * @return
     */
    public JSONObject getRequestFlux(String url,
                                           MultiValueMap<String, String> headers,
                                           Map<String, Object> params) {
        Flux<JSONObject> flux = webClientBuilder().defaultHeaders(httpHeaders -> {
            httpHeaders.addAll(headers);
        }).build()
                // 方法调用, WebClient中提供了多种方法
                .method(HttpMethod.GET)
                // 请求URL
                .uri(url, params)
                // 获取响应结果
                .retrieve()
                // 将结果转换为指定类型
                .bodyToFlux(JSONObject.class)
                .doOnError(e -> log.error("Error occurred", e))
                .onErrorResume(e -> Mono.just(new JSONObject().fluentPut("message", "Fallback value")));
        // 使用subscribe异步订阅结果
        flux.subscribe(result -> System.out.println(result));
        // 使用block同步阻塞,返回响应结果
        return flux.blockFirst();
    }



    /**
     * get请求, 带参
     * @param url
     * @param params
     * @return
     */
    public JSONObject getRequest(String url,
                                 Map<String, Object> params) {
        Mono<JSONObject> mono = webClientBuilder().build()
                // 方法调用, WebClient中提供了多种方法
                .method(HttpMethod.GET)
                // 请求URL
                .uri(url, params)
                // 获取响应结果
                .retrieve()
                // 将结果转换为指定类型
                .bodyToMono(JSONObject.class)
                .doOnError(e -> log.error("Error occurred", e))
                .onErrorResume(e -> Mono.just(new JSONObject().fluentPut("message", "Fallback value")));
        // 使用subscribe异步订阅结果
        mono.subscribe(result -> System.out.println(result));
        // 使用block同步阻塞,返回响应结果
        return mono.block();
    }

    /**
     * get请求, 不带参
     * @param url
     * @return
     */
    public JSONObject getRequest(String url) {
        Mono<JSONObject> mono = webClientBuilder().build()
                // 方法调用, WebClient中提供了多种方法
                .method(HttpMethod.GET)
                // 请求URL
                .uri(url)
                // 获取响应结果
                .retrieve()
                // 将结果转换为指定类型
                .bodyToMono(JSONObject.class)
                .doOnError(e -> log.error("Error occurred", e))
                .onErrorResume(e -> Mono.just(new JSONObject().fluentPut("message", "Fallback value")));
        // 使用subscribe异步订阅结果
        mono.subscribe(result -> System.out.println(result));
        // 使用block同步阻塞,返回响应结果
        return mono.block();
    }

    /**
     * post请求, 请求头, 请求内容
     * @param url
     * @param headers
     * @param body
     * @return
     */
    public JSONObject postRequest(String url,
                                  MultiValueMap<String, String> headers,
                                  Map<String, Object> body) {
        Mono<JSONObject> mono = webClientBuilder().build().post().uri(url).headers(httpHeaders -> {
            httpHeaders.addAll(headers);
        }).contentType(MediaType.APPLICATION_JSON)
                .bodyValue(body)
                .retrieve()
                .bodyToMono(JSONObject.class)
                .doOnError(e -> log.error("Error occurred", e))
                .onErrorResume(e -> Mono.just(new JSONObject().fluentPut("message", "Fallback value")));
        // 使用subscribe异步订阅结果
        mono.subscribe(result -> System.out.println(result));
        // 使用block同步阻塞,返回响应结果
        return mono.block();
    }


    /**
     * post请求, 请求头, 请求内容
     * @param url
     * @param body
     * @return
     */
    public JSONObject postRequest(String url,
                                  Map<String, Object> body) {
        Mono<JSONObject> mono = webClientBuilder().build().post().uri(url)
                .contentType(MediaType.APPLICATION_JSON)
                .bodyValue(body)
                .retrieve()
                .bodyToMono(JSONObject.class)
                .doOnError(e -> log.error("Error occurred", e))
                .onErrorResume(e -> Mono.just(new JSONObject().fluentPut("message", "Fallback value")));
        // 使用subscribe异步订阅结果
        mono.subscribe(result -> System.out.println(result));
        // 使用block同步阻塞,返回响应结果
        return mono.block();
    }

    /**
     * post请求, FormData/Multipart请求方式
     * @param url
     * @param headers
     * @param params
     * 传递文件还可以使用如下方式进行构造
     *
     * @param <T>
     * @return
     */
    public <T> JSONObject postRequestMultipart(String url,
                                  MultiValueMap headers,
                                  MultiValueMap params) {
        Mono<JSONObject> mono = webClient().post().uri(url).headers(httpHeaders -> {
            httpHeaders.addAll(headers);
        }).body(fromMultipartData(params))
          .retrieve()
          .bodyToMono(JSONObject.class)
          .doOnError(e -> log.error("Error occurred", e))
          .onErrorResume(e -> Mono.just(new JSONObject().fluentPut("message", "Fallback value")));
        // 使用subscribe异步订阅结果
        mono.subscribe(result -> System.out.println(result));
        // 使用block同步阻塞,返回响应结果
        return mono.block();
    }

    /**
     * post请求, FormData/Multipart请求方式
     * @param url
     * @param headers
     * @param params
     *      * MultipartBodyBuilder builder = new MultipartBodyBuilder();
     *      * builder.part("fieldPart", "fieldValue");
     *      * builder.part("filePart1", new FileSystemResource("...logo.png")); // 本地文件
     *      * builder.part("jsonPart", new Person("Jason"));
     *      * builder.part("myPart", part); // Part from a server request
     *      *
     *      * MultiValueMap<String, HttpEntity<?>> parts = builder.build();
     *
     * @param <T>
     * @return
     */
    public <T> JSONObject postRequestMultipart(String url,
                                               MultiValueMap headers,
                                               MultipartBodyBuilder params) {
        Mono<JSONObject> mono = webClient().post().uri(url).headers(httpHeaders -> {
            httpHeaders.addAll(headers);
        }).body(fromMultipartData(params.build()))
                .retrieve()
                .bodyToMono(JSONObject.class)
                .doOnError(e -> log.error("Error occurred", e))
                .onErrorResume(e -> Mono.just(new JSONObject().fluentPut("message", "Fallback value")));
        // 使用subscribe异步订阅结果
        mono.subscribe(result -> System.out.println(result));
        // 使用block同步阻塞,返回响应结果
        return mono.block();
    }

    /**
     * 多请求结果组合
     */
//    public void test1() {
//        WebClient webClient = WebClient.create();
//        Mono<String> responseOne = webClient.get()
//                .uri("http://example.com/endpointOne")
//                .retrieve()
//                .bodyToMono(String.class);
//
//        Mono<String> responseTwo = webClient.get()
//                .uri("http://example.com/endpointTwo")
//                .retrieve()
//                .bodyToMono(String.class);
//
//// Use Mono.zip to execute requests concurrently
//        Mono.zip(responseOne, responseTwo).subscribe(results -> {
//            System.out.println("Result 1: " + results.getT1());
//            System.out.println("Result 2: " + results.getT2());
//        });
//    }

    /**
     * 多请求结果组合
     */
    //public void test2() {
    //    WebClient client =  WebClient.create("http://www.kailing.pub");
    //    Mono<String> result1Mono = client .get()
    //            .uri("/article/index/arcid/{id}.html", 255)
    //            .retrieve()
    //            .bodyToMono(String.class);
    //    Mono<String> result2Mono = client .get()
    //            .uri("/article/index/arcid/{id}.html", 254)
    //            .retrieve()
    //            .bodyToMono(String.class);
    //    Map  map = Mono.zip(result1Mono, result2Mono, (result1, result2) -> {
    //        Map arrayList = new HashMap<>();
    //        arrayList.put("result1", result1);
    //        arrayList.put("result2", result2);
    //        return arrayList;
    //    }).block();
    //    System.err.println(map.toString());
    //}


    @Bean
    public WebClient.Builder webClientBuilder() {
        return WebClient.builder();
    }

    @Bean
    public WebClient webClient() {
        return WebClient.create();
    }
}
